function mxConnectionHandler(a, b) {
    if (a != null) {
        this.graph = a;
        this.factoryMethod = b;
        this.init();
    }
}
mxConnectionHandler.prototype = new mxEventSource;
mxConnectionHandler.prototype.constructor = mxConnectionHandler;
mxConnectionHandler.prototype.graph = null;
mxConnectionHandler.prototype.factoryMethod = !0;
mxConnectionHandler.prototype.moveIconFront = !1;
mxConnectionHandler.prototype.moveIconBack = !1;
mxConnectionHandler.prototype.connectImage = null;
mxConnectionHandler.prototype.targetConnectImage = !1;
mxConnectionHandler.prototype.enabled = !0;
mxConnectionHandler.prototype.select = !0;
mxConnectionHandler.prototype.createTarget = !1;
mxConnectionHandler.prototype.marker = null;
mxConnectionHandler.prototype.constraintHandler = null;
mxConnectionHandler.prototype.error = null;
mxConnectionHandler.prototype.waypointsEnabled = !1;
mxConnectionHandler.prototype.tapAndHoldEnabled = !0;
mxConnectionHandler.prototype.tapAndHoldDelay = 500;
mxConnectionHandler.prototype.tapAndHoldInProgress = !1;
mxConnectionHandler.prototype.tapAndHoldValid = !1;
mxConnectionHandler.prototype.tapAndHoldTolerance = 4;
mxConnectionHandler.prototype.initialTouchX = 0;
mxConnectionHandler.prototype.initialTouchY = 0;
mxConnectionHandler.prototype.ignoreMouseDown = !1;
mxConnectionHandler.prototype.first = null;
mxConnectionHandler.prototype.connectIconOffset = new mxPoint(0, mxConstants.TOOLTIP_VERTICAL_OFFSET);
mxConnectionHandler.prototype.edgeState = null;
mxConnectionHandler.prototype.changeHandler = null;
mxConnectionHandler.prototype.drillHandler = null;
mxConnectionHandler.prototype.mouseDownCounter = 0;
mxConnectionHandler.prototype.movePreviewAway = mxClient.IS_IE;
mxConnectionHandler.prototype.isEnabled = function() {
    return this.enabled
};
mxConnectionHandler.prototype.setEnabled = function(a) {
    this.enabled = a
};
mxConnectionHandler.prototype.isCreateTarget = function() {
    return this.createTarget
};
mxConnectionHandler.prototype.setCreateTarget = function(a) {
    this.createTarget = a
};
mxConnectionHandler.prototype.createShape = function() {
    var a = new mxPolyline([], mxConstants.INVALID_COLOR);
    a.isDashed = true;
    a.dialect = this.graph.dialect != mxConstants.DIALECT_SVG ? mxConstants.DIALECT_VML: mxConstants.DIALECT_SVG;
    a.init(this.graph.getView().getOverlayPane());
    if (this.graph.dialect == mxConstants.DIALECT_SVG) {
        a.pipe.setAttribute("style", "pointer-events:none;");
        a.innerNode.setAttribute("style", "pointer-events:none;")
    } else {
        var b = mxUtils.bind(this,
        function(a) {
            a = mxUtils.convertPoint(this.graph.container, mxEvent.getClientX(a), mxEvent.getClientY(a));
            return this.graph.view.getState(this.graph.getCellAt(a.x, a.y))
        });
        mxEvent.redirectMouseEvents(a.node, this.graph, b)
    }
    return a
};
mxConnectionHandler.prototype.init = function() {
    this.graph.addMouseListener(this);
    this.marker = this.createMarker();
    this.constraintHandler = new mxConstraintHandler(this.graph);
    this.changeHandler = mxUtils.bind(this,
    function() {
        if (this.iconState != null) this.iconState = this.graph.getView().getState(this.iconState.cell);
        if (this.iconState != null) this.redrawIcons(this.icons, this.iconState);
        else {
            this.destroyIcons(this.icons);
            this.previous = null
        }
        this.constraintHandler.reset()
    });
    this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
    this.graph.getView().addListener(mxEvent.SCALE, this.changeHandler);
    this.graph.getView().addListener(mxEvent.TRANSLATE, this.changeHandler);
    this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.changeHandler);
    this.drillHandler = mxUtils.bind(this,
    function() {
        this.destroyIcons(this.icons)
    });
    this.graph.addListener(mxEvent.START_EDITING, this.drillHandler);
    this.graph.getView().addListener(mxEvent.DOWN, this.drillHandler);
    this.graph.getView().addListener(mxEvent.UP, this.drillHandler)
};
mxConnectionHandler.prototype.isConnectableCell = function() {
    return true
};
mxConnectionHandler.prototype.createMarker = function() {
    var a = new mxCellMarker(this.graph);
    a.hotspotEnabled = true;
    a.getCell = mxUtils.bind(this,
    function(b, c) {
        c = mxCellMarker.prototype.getCell.apply(a, arguments);
        this.error = null;
        if (!this.isConnectableCell(c)) return null;
        if (c != null) if (this.isConnecting()) {
            if (this.previous != null) {
                this.error = this.validateConnection(this.previous.cell, c);
                if (this.error != null && this.error.length == 0) {
                    c = null;
                    if (this.isCreateTarget()) this.error = null
                }
            }
        } else this.isValidSource(c) || (c = null);
        else if (this.isConnecting() && !this.isCreateTarget() && !this.graph.allowDanglingEdges) this.error = "";
        return c
    });
    a.isValidState = mxUtils.bind(this,
    function(b) {
        return this.isConnecting() ? this.error == null: mxCellMarker.prototype.isValidState.apply(a, arguments)
    });
    a.getMarkerColor = mxUtils.bind(this,
    function(b, c, d) {
        return this.connectImage == null || this.isConnecting() ? mxCellMarker.prototype.getMarkerColor.apply(a, arguments) : null
    });
    a.intersects = mxUtils.bind(this,
    function(b, c) {
        return this.connectImage != null || this.isConnecting() ? true: mxCellMarker.prototype.intersects.apply(a, arguments)
    });
    return a
};
mxConnectionHandler.prototype.start = function(a, b, c, d) {
    this.previous = a;
    this.first = new mxPoint(b, c);
    this.edgeState = d != null ? d: this.createEdgeState(null);
    this.marker.currentColor = this.marker.validColor;
    this.marker.markedState = a;
    this.marker.mark();
    this.fireEvent(new mxEventObject(mxEvent.START, "state", this.previous))
};
mxConnectionHandler.prototype.isConnecting = function() {
    return this.first != null && this.shape != null
};
mxConnectionHandler.prototype.isValidSource = function(a) {
    return this.graph.isValidSource(a)
};
mxConnectionHandler.prototype.isValidTarget = function() {
    return true
};
mxConnectionHandler.prototype.validateConnection = function(a, b) {
    return ! this.isValidTarget(b) ? "": this.graph.getEdgeValidationError(null, a, b)
};
mxConnectionHandler.prototype.getConnectImage = function() {
    return this.connectImage
};
mxConnectionHandler.prototype.isMoveIconToFrontForState = function(a) {
    return a.text != null && a.text.node.parentNode == this.graph.container ? true: this.moveIconFront
};
mxConnectionHandler.prototype.createIcons = function(a) {
    var b = this.getConnectImage(a);
    if (b != null && a != null) {
        this.iconState = a;
        var c = [],
        d = new mxRectangle(0, 0, b.width, b.height),
        e = new mxImageShape(d, b.src, null, null, 0);
        e.preserveImageAspect = false;
        if (this.isMoveIconToFrontForState(a)) {
            e.dialect = mxConstants.DIALECT_STRICTHTML;
            e.init(this.graph.container)
        } else {
            e.dialect = this.graph.dialect == mxConstants.DIALECT_SVG ? mxConstants.DIALECT_SVG: mxConstants.DIALECT_VML;
            e.init(this.graph.getView().getOverlayPane());
            this.moveIconBack && e.node.previousSibling != null && e.node.parentNode.insertBefore(e.node, e.node.parentNode.firstChild)
        }
        e.node.style.cursor = mxConstants.CURSOR_CONNECT;
        var f = mxUtils.bind(this,
        function() {
            return this.currentState != null ? this.currentState: a
        }),
        b = mxUtils.bind(this,
        function(a) {
            if (!mxEvent.isConsumed(a)) {
                this.icon = e;
                this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(a, f()))
            }
        });
        mxEvent.redirectMouseEvents(e.node, this.graph, f, b);
        c.push(e);
        this.redrawIcons(c, this.iconState);
        return c
    }
    return null
};
mxConnectionHandler.prototype.redrawIcons = function(a, b) {
    if (a != null && a[0] != null && b != null) {
        var c = this.getIconPosition(a[0], b);
        a[0].bounds.x = c.x;
        a[0].bounds.y = c.y;
        a[0].redraw()
    }
};
mxConnectionHandler.prototype.getIconPosition = function(a, b) {
    var c = this.graph.getView().scale,
    d = b.getCenterX(),
    e = b.getCenterY();
    if (this.graph.isSwimlane(b.cell)) var f = this.graph.getStartSize(b.cell),
    d = f.width != 0 ? b.x + f.width * c / 2 : d,
    e = f.height != 0 ? b.y + f.height * c / 2 : e;
    return new mxPoint(d - a.bounds.width / 2, e - a.bounds.height / 2)
};
mxConnectionHandler.prototype.destroyIcons = function(a) {
    if (a != null) {
        this.iconState = null;
        for (var b = 0; b < a.length; b++) a[b].destroy()
    }
};
mxConnectionHandler.prototype.isStartEvent = function(a) {
    return ! this.graph.isForceMarqueeEvent(a.getEvent()) && (this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null || this.previous != null && this.error == null && (this.icons == null || this.icons != null && this.icon != null))
};
mxConnectionHandler.prototype.mouseDown = function(a, b) {
    this.mouseDownCounter++;
    if (this.isEnabled() && this.graph.isEnabled() && !b.isConsumed() && !this.isConnecting() && this.isStartEvent(b)) {
        if (this.constraintHandler.currentConstraint != null && this.constraintHandler.currentFocus != null && this.constraintHandler.currentPoint != null) {
            this.sourceConstraint = this.constraintHandler.currentConstraint;
            this.previous = this.constraintHandler.currentFocus;
            this.first = this.constraintHandler.currentPoint.clone()
        } else this.first = new mxPoint(b.getGraphX(), b.getGraphY());
        this.edgeState = this.createEdgeState(b);
        this.mouseDownCounter = 1;
        if (this.waypointsEnabled && this.shape == null) {
            this.waypoints = null;
            this.shape = this.createShape()
        }
        this.previous == null && this.edgeState != null && this.edgeState.cell.geometry.setTerminalPoint(this.graph.getPointForEvent(b.getEvent()), true);
        this.fireEvent(new mxEventObject(mxEvent.START, "state", this.previous));
        b.consume()
    } else if (mxClient.IS_TOUCH && this.tapAndHoldEnabled && !this.tapAndHoldInProgress && this.isEnabled() && this.graph.isEnabled() && !this.isConnecting()) {
        this.tapAndHoldInProgress = true;
        this.initialTouchX = b.getX();
        this.initialTouchY = b.getY();
        var c = this.graph.view.getState(this.marker.getCell(b));
        this.tapAndHoldThread && window.clearTimeout(this.tapAndHoldThread);
        this.tapAndHoldThread = window.setTimeout(mxUtils.bind(this,
        function() {
            this.tapAndHoldValid && this.tapAndHold(b, c);
            this.tapAndHoldValid = this.tapAndHoldInProgress = false
        }), this.tapAndHoldDelay);
        this.tapAndHoldValid = true
    }
    this.selectedIcon = this.icon;
    this.icon = null
};
mxConnectionHandler.prototype.tapAndHold = function(a, b) {
    if (b != null) {
        this.marker.currentColor = this.marker.validColor;
        this.marker.markedState = b;
        this.marker.mark();
        this.first = new mxPoint(a.getGraphX(), a.getGraphY());
        this.edgeState = this.createEdgeState(a);
        this.previous = b;
        this.fireEvent(new mxEventObject(mxEvent.START, "state", this.previous))
    }
};
mxConnectionHandler.prototype.isImmediateConnectSource = function(a) {
    return ! this.graph.isCellMovable(a.cell)
};
mxConnectionHandler.prototype.createEdgeState = function() {
    return null
};
mxConnectionHandler.prototype.updateCurrentState = function(a) {
    var b = this.marker.process(a);
    this.constraintHandler.update(a, this.first == null);
    this.currentState = b
};
mxConnectionHandler.prototype.convertWaypoint = function(a) {
    var b = this.graph.getView().getScale(),
    c = this.graph.getView().getTranslate();
    a.x = a.x / b - c.x;
    a.y = a.y / b - c.y
};
mxConnectionHandler.prototype.mouseMove = function(a, b) {
    if (this.tapAndHoldValid) this.tapAndHoldValid = Math.abs(this.initialTouchX - b.getX()) < this.tapAndHoldTolerance && Math.abs(this.initialTouchY - b.getY()) < this.tapAndHoldTolerance;
    if (!b.isConsumed() && (this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown)) {
        if (!this.isEnabled() && this.currentState != null) {
            this.destroyIcons(this.icons);
            this.currentState = null
        } (this.first != null || this.isEnabled() && this.graph.isEnabled()) && this.updateCurrentState(b);
        if (this.first != null) {
            var c = this.graph.getView().scale,
            c = new mxPoint(this.graph.snap(b.getGraphX() / c) * c, this.graph.snap(b.getGraphY() / c) * c),
            d = null,
            e = c;
            if (this.constraintHandler.currentConstraint != null && this.constraintHandler.currentFocus != null && this.constraintHandler.currentPoint != null) {
                d = this.constraintHandler.currentConstraint;
                e = this.constraintHandler.currentPoint.clone()
            }
            var f = this.first;
            if (this.selectedIcon != null) {
                var g = this.selectedIcon.bounds.width,
                h = this.selectedIcon.bounds.height;
                if (this.currentState != null && this.targetConnectImage) {
                    g = this.getIconPosition(this.selectedIcon, this.currentState);
                    this.selectedIcon.bounds.x = g.x;
                    this.selectedIcon.bounds.y = g.y
                } else this.selectedIcon.bounds = new mxRectangle(b.getGraphX() + this.connectIconOffset.x, b.getGraphY() + this.connectIconOffset.y, g, h);
                this.selectedIcon.redraw()
            }
            if (this.edgeState != null) {
                this.edgeState.absolutePoints = [null, this.currentState != null ? null: e];
                this.graph.view.updateFixedTerminalPoint(this.edgeState, this.previous, true, this.sourceConstraint);
                if (this.currentState != null) {
                    d == null && (d = this.graph.getConnectionConstraint(this.edgeState, this.previous, false));
                    this.edgeState.setAbsoluteTerminalPoint(null, false);
                    this.graph.view.updateFixedTerminalPoint(this.edgeState, this.currentState, false, d)
                }
                f = null;
                if (this.waypoints != null) {
                    f = [];
                    for (e = 0; e < this.waypoints.length; e++) {
                        d = this.waypoints[e].clone();
                        this.convertWaypoint(d);
                        f[e] = d
                    }
                }
                this.graph.view.updatePoints(this.edgeState, f, this.previous, this.currentState);
                this.graph.view.updateFloatingTerminalPoints(this.edgeState, this.previous, this.currentState);
                e = this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 1];
                f = this.edgeState.absolutePoints[0]
            } else {
                if (this.currentState != null && this.constraintHandler.currentConstraint == null) {
                    d = this.getTargetPerimeterPoint(this.currentState, b);
                    d != null && (e = d)
                }
                if (this.sourceConstraint == null && this.previous != null) {
                    d = this.getSourcePerimeterPoint(this.previous, this.waypoints != null && this.waypoints.length > 0 ? this.waypoints[0] : e, b);
                    d != null && (f = d)
                }
            }
            if (this.currentState == null && this.movePreviewAway) {
                d = e.x - f.x;
                g = e.y - f.y;
                h = Math.sqrt(d * d + g * g);
                e.x = e.x - d * 4 / h;
                e.y = e.y - g * 4 / h
            }
            if (this.shape == null) {
                d = Math.abs(c.x - this.first.x);
                g = Math.abs(c.y - this.first.y);
                if (d > this.graph.tolerance || g > this.graph.tolerance) {
                    this.shape = this.createShape();
                    this.updateCurrentState(b)
                }
            }
            if (this.shape != null) {
                if (this.edgeState != null) this.shape.points = this.edgeState.absolutePoints;
                else {
                    c = [f];
                    this.waypoints != null && (c = c.concat(this.waypoints));
                    c.push(e);
                    this.shape.points = c
                }
                this.drawPreview()
            }
            mxEvent.consume(b.getEvent());
            b.consume()
        } else if (!this.isEnabled() || !this.graph.isEnabled()) this.constraintHandler.reset();
        else if (this.previous != this.currentState && this.edgeState == null) {
            this.destroyIcons(this.icons);
            this.icons = null;
            if (this.currentState != null && this.error == null) {
                this.icons = this.createIcons(this.currentState);
                if (this.icons == null) {
                    this.currentState.setCursor(mxConstants.CURSOR_CONNECT);
                    b.consume()
                }
            }
            this.previous = this.currentState
        } else this.previous == this.currentState && (this.currentState != null && this.icons == null && !this.graph.isMouseDown) && b.consume();
        this.constraintHandler.currentConstraint != null && this.marker.reset();
        if (!this.graph.isMouseDown && this.currentState != null && this.icons != null) {
            c = false;
            f = b.getSource();
            for (e = 0; e < this.icons.length && !c; e++) c = f == this.icons[e].node || f.parentNode == this.icons[e].node;
            c || this.updateIcons(this.currentState, this.icons, b)
        }
    } else this.constraintHandler.reset()
};
mxConnectionHandler.prototype.getTargetPerimeterPoint = function(a) {
    var b = null,
    c = a.view,
    d = c.getPerimeterFunction(a);
    if (d != null) {
        var e = this.waypoints != null && this.waypoints.length > 0 ? this.waypoints[this.waypoints.length - 1] : new mxPoint(this.previous.getCenterX(), this.previous.getCenterY()),
        a = d(c.getPerimeterBounds(a), this.edgeState, e, false);
        a != null && (b = a)
    } else b = new mxPoint(a.getCenterX(), a.getCenterY());
    return b
};
mxConnectionHandler.prototype.getSourcePerimeterPoint = function(a, b) {
    var c = null,
    d = a.view,
    e = d.getPerimeterFunction(a);
    if (e != null) {
        d = e(d.getPerimeterBounds(a), a, b, false);
        d != null && (c = d)
    } else c = new mxPoint(a.getCenterX(), a.getCenterY());
    return c
};
mxConnectionHandler.prototype.updateIcons = function() {};
mxConnectionHandler.prototype.isStopEvent = function(a) {
    return a.getState() != null
};
mxConnectionHandler.prototype.addWaypointForEvent = function(a) {
    var b = mxUtils.convertPoint(this.graph.container, a.getX(), a.getY()),
    c = Math.abs(b.x - this.first.x),
    b = Math.abs(b.y - this.first.y);
    if (this.waypoints != null || this.mouseDownCounter > 1 && (c > this.graph.tolerance || b > this.graph.tolerance)) {
        if (this.waypoints == null) this.waypoints = [];
        c = this.graph.view.scale;
        b = new mxPoint(this.graph.snap(a.getGraphX() / c) * c, this.graph.snap(a.getGraphY() / c) * c);
        this.waypoints.push(b)
    }
};

/**
 * 鼠标弹出事件
 */
mxConnectionHandler.prototype.mouseUp = function(a, b) {
    if (!b.isConsumed() && this.isConnecting()) {
        if (this.waypointsEnabled && !this.isStopEvent(b)) {
            this.addWaypointForEvent(b);
            b.consume();
            return
        }
        if (this.error == null) {
        	//post to server by:tk
        	//postCellConnect('鼠标弹起了');
        	
            var c = this.previous != null ? this.previous.cell: null,
            d = null;
            if (this.constraintHandler.currentConstraint != null && this.constraintHandler.currentFocus != null) d = this.constraintHandler.currentFocus.cell;
            if (d == null && this.marker.hasValidState()) d = this.marker.validState.cell;
            this.connect(c, d, b.getEvent(), b.getCell())
        } else {
            this.previous != null && (this.marker.validState != null && this.previous.cell == this.marker.validState.cell) && this.graph.selectCellForEvent(this.marker.source, evt);
            this.error.length > 0 && this.graph.validationAlert(this.error)
        }
        this.destroyIcons(this.icons);
        b.consume()
    }
    this.first != null && this.reset();
    this.tapAndHoldValid = this.tapAndHoldInProgress = false
};
mxConnectionHandler.prototype.reset = function() {
    if (this.shape != null) {
        this.shape.destroy();
        this.shape = null
    }
    this.destroyIcons(this.icons);
    this.icons = null;
    this.marker.reset();
    this.constraintHandler.reset();
    this.sourceConstraint = this.error = this.previous = this.edgeState = this.selectedIcon = null;
    this.mouseDownCounter = 0;
    this.icon = this.first = null;
    this.fireEvent(new mxEventObject(mxEvent.RESET))
};
mxConnectionHandler.prototype.drawPreview = function() {
    var a = this.error == null,
    b = this.getEdgeColor(a);
    this.shape.dialect == mxConstants.DIALECT_SVG ? this.shape.innerNode.setAttribute("stroke", b) : this.shape.node.strokecolor = b;
    this.shape.strokewidth = this.getEdgeWidth(a);
    this.shape.redraw();
    mxUtils.repaintGraph(this.graph, this.shape.points[1])
};
mxConnectionHandler.prototype.getEdgeColor = function(a) {
    return a ? mxConstants.VALID_COLOR: mxConstants.INVALID_COLOR
};
mxConnectionHandler.prototype.getEdgeWidth = function(a) {
    return a ? 3 : 1
};
mxConnectionHandler.prototype.connect = function(a, b, c, d) {
    if (b != null || this.isCreateTarget() || this.graph.allowDanglingEdges) {
        var e = this.graph.getModel(),
        f = null;
        e.beginUpdate();
        try {
            if (a != null && b == null && this.isCreateTarget()) {
                b = this.createTargetVertex(c, a);
                if (b != null) {
                    d = this.graph.getDropTarget([b], c, d);
                    if (d == null || !this.graph.getModel().isEdge(d)) {
                        var g = this.graph.getView().getState(d);
                        if (g != null) {
                            var h = e.getGeometry(b);
                            h.x = h.x - g.origin.x;
                            h.y = h.y - g.origin.y
                        }
                    } else d = this.graph.getDefaultParent();
                    this.graph.addCell(b, d)
                }
            }
            var k = this.graph.getDefaultParent();
            if (a != null && b != null && e.getParent(a) == e.getParent(b) && e.getParent(e.getParent(a)) != e.getRoot()) {
                k = e.getParent(a);
                a.geometry != null && a.geometry.relative && (b.geometry != null && b.geometry.relative) && (k = e.getParent(k))
            }
            h = g = null;
            if (this.edgeState != null) {
                g = this.edgeState.cell.value;
                h = this.edgeState.cell.style
            }
            f = this.insertEdge(k, null, g, a, b, h);
            if (f != null) {
                this.graph.setConnectionConstraint(f, a, true, this.sourceConstraint);
                this.graph.setConnectionConstraint(f, b, false, this.constraintHandler.currentConstraint);
                this.edgeState != null && e.setGeometry(f, this.edgeState.cell.geometry);
                var i = e.getGeometry(f);
                if (i == null) {
                    i = new mxGeometry;
                    i.relative = true;
                    e.setGeometry(f, i)
                }
                if (this.waypoints != null && this.waypoints.length > 0) {
                    var l = this.graph.view.scale,
                    m = this.graph.view.translate;
                    i.points = [];
                    for (a = 0; a < this.waypoints.length; a++) {
                        var n = this.waypoints[a];
                        i.points.push(new mxPoint(n.x / l - m.x, n.y / l - m.y))
                    }
                }
                if (b == null) {
                    n = this.graph.getPointForEvent(c, false);
                    n.x = n.x - this.graph.panDx / this.graph.view.scale;
                    n.y = n.y - this.graph.panDy / this.graph.view.scale;
                    i.setTerminalPoint(n, false)
                }
                this.fireEvent(new mxEventObject(mxEvent.CONNECT, "cell", f, "event", c, "target", d))
            }
        } catch(o) {
            mxLog.show();
            mxLog.debug(o.message)
        } finally {
            e.endUpdate()
        }
        this.select && this.selectCells(f, b)
    }
};
mxConnectionHandler.prototype.selectCells = function(a) {
    this.graph.setSelectionCell(a)
};
mxConnectionHandler.prototype.insertEdge = function(a, b, c, d, e, f) {
    if (this.factoryMethod == null) return this.graph.insertEdge(a, b, c, d, e, f);
    b = this.createEdge(c, d, e, f);
    return b = this.graph.addEdge(b, a, d, e)
};
mxConnectionHandler.prototype.createTargetVertex = function(a, b) {
    for (var c = this.graph.getCellGeometry(b); c != null && c.relative;) {
        b = this.graph.getModel().getParent(b);
        c = this.graph.getCellGeometry(b)
    }
    var d = this.graph.cloneCells([b])[0],
    c = this.graph.getModel().getGeometry(d);
    if (c != null) {
        var e = this.graph.getPointForEvent(a);
        c.x = this.graph.snap(e.x - c.width / 2) - this.graph.panDx / this.graph.view.scale;
        c.y = this.graph.snap(e.y - c.height / 2) - this.graph.panDy / this.graph.view.scale;
        if (this.first != null) {
            var f = this.graph.view.getState(b);
            if (f != null) {
                var g = this.getAlignmentTolerance();
                if (Math.abs(this.graph.snap(this.first.x) - this.graph.snap(e.x)) <= g) c.x = f.x;
                else if (Math.abs(this.graph.snap(this.first.y) - this.graph.snap(e.y)) <= g) c.y = f.y
            }
        }
    }
    return d
};
mxConnectionHandler.prototype.getAlignmentTolerance = function() {
    return this.graph.isGridEnabled() ? this.graph.gridSize: this.graph.tolerance
};
mxConnectionHandler.prototype.createEdge = function(a, b, c, d) {
    var e = null;
    this.factoryMethod != null && (e = this.factoryMethod(b, c, d));
    if (e == null) {
        e = new mxCell(a || "");
        e.setEdge(true);
        e.setStyle(d);
        a = new mxGeometry;
        a.relative = true;
        e.setGeometry(a)
    }
    return e
};
mxConnectionHandler.prototype.destroy = function() {
    this.graph.removeMouseListener(this);
    if (this.shape != null) {
        this.shape.destroy();
        this.shape = null
    }
    if (this.marker != null) {
        this.marker.destroy();
        this.marker = null
    }
    if (this.constraintHandler != null) {
        this.constraintHandler.destroy();
        this.constraintHandler = null
    }
    if (this.changeHandler != null) {
        this.graph.getModel().removeListener(this.changeHandler);
        this.graph.getView().removeListener(this.changeHandler);
        this.changeHandler = null
    }
    if (this.drillHandler != null) {
        this.graph.removeListener(this.drillHandler);
        this.graph.getView().removeListener(this.drillHandler);
        this.drillHandler = null
    }
};
function mxConstraintHandler(a) {
    this.graph = a
}
mxConstraintHandler.prototype.pointImage = new mxImage(mxClient.imageBasePath + "/point.gif", 5, 5);
mxConstraintHandler.prototype.graph = null;
mxConstraintHandler.prototype.enabled = !0;
mxConstraintHandler.prototype.highlightColor = mxConstants.DEFAULT_VALID_COLOR;
mxConstraintHandler.prototype.isEnabled = function() {
    return this.enabled
};
mxConstraintHandler.prototype.setEnabled = function(a) {
    this.enabled = a
};
mxConstraintHandler.prototype.reset = function() {
    if (this.focusIcons != null) {
        for (var a = 0; a < this.focusIcons.length; a++) this.focusIcons[a].destroy();
        this.focusIcons = null
    }
    if (this.focusHighlight != null) {
        this.focusHighlight.destroy();
        this.focusHighlight = null
    }
    this.focusPoints = this.currentFocus = this.currentPoint = this.currentFocusArea = this.currentConstraint = null
};
mxConstraintHandler.prototype.getTolerance = function() {
    return this.graph.getTolerance()
};
mxConstraintHandler.prototype.getImageForConstraint = function() {
    return this.pointImage
};
mxConstraintHandler.prototype.update = function(a, b) {
    if (this.isEnabled()) {
        var c = this.getTolerance(),
        d = new mxRectangle(a.getGraphX() - c, a.getGraphY() - c, 2 * c, 2 * c),
        e = a.getCell() != null ? this.graph.isCellConnectable(a.getCell()) : false;
        if (this.currentFocusArea == null || !mxUtils.intersects(this.currentFocusArea, d) || a.getState() != null && this.currentFocus != null && e && this.graph.getModel().getParent(a.getCell()) == this.currentFocus.cell) {
            this.currentFocusArea = null;
            if (a.getState() != this.currentFocus) {
                this.currentFocus = null;
                this.constraints = a.getState() != null && e ? this.graph.getAllConnectionConstraints(a.getState(), b) : null;
                if (this.constraints != null) {
                    this.currentFocus = a.getState();
                    this.currentFocusArea = new mxRectangle(a.getState().x, a.getState().y, a.getState().width, a.getState().height);
                    if (this.focusIcons != null) {
                        for (e = 0; e < this.focusIcons.length; e++) this.focusIcons[e].destroy();
                        this.focusPoints = this.focusIcons = null
                    }
                    this.focusIcons = [];
                    this.focusPoints = [];
                    for (e = 0; e < this.constraints.length; e++) {
                        var f = this.graph.getConnectionPoint(a.getState(), this.constraints[e]),
                        g = this.getImageForConstraint(a.getState(), this.constraints[e], f),
                        h = g.src,
                        g = new mxRectangle(f.x - g.width / 2, f.y - g.height / 2, g.width, g.height),
                        g = new mxImageShape(g, h);
                        g.dialect = this.graph.dialect == mxConstants.DIALECT_SVG ? mxConstants.DIALECT_SVG: mxConstants.DIALECT_VML;
                        g.init(this.graph.getView().getOverlayPane());
                        g.node.previousSibling != null && g.node.parentNode.insertBefore(g.node, g.node.parentNode.firstChild);
                        h = mxUtils.bind(this,
                        function() {
                            return this.currentFocus != null ? this.currentFocus: a.getState()
                        });
                        g.redraw();
                        mxEvent.redirectMouseEvents(g.node, this.graph, h);
                        this.currentFocusArea.add(g.bounds);
                        this.focusIcons.push(g);
                        this.focusPoints.push(f)
                    }
                    this.currentFocusArea.grow(c)
                } else if (this.focusIcons != null) {
                    if (this.focusHighlight != null) {
                        this.focusHighlight.destroy();
                        this.focusHighlight = null
                    }
                    for (e = 0; e < this.focusIcons.length; e++) this.focusIcons[e].destroy();
                    this.focusPoints = this.focusIcons = null
                }
            }
        }
        this.currentPoint = this.currentConstraint = null;
        if (this.focusIcons != null && this.constraints != null && (a.getState() == null || this.currentFocus == a.getState())) for (e = 0; e < this.focusIcons.length; e++) if (mxUtils.intersects(this.focusIcons[e].bounds, d)) {
            this.currentConstraint = this.constraints[e];
            this.currentPoint = this.focusPoints[e];
            c = this.focusIcons[e].bounds.clone();
            c.grow(mxClient.IS_IE ? 3 : 2);
            if (mxClient.IS_IE) {
                c.width = c.width - 1;
                c.height = c.height - 1
            }
            if (this.focusHighlight == null) {
                c = new mxRectangleShape(c, null, this.highlightColor, 3);
                c.dialect = this.graph.dialect == mxConstants.DIALECT_SVG ? mxConstants.DIALECT_SVG: mxConstants.DIALECT_VML;
                c.init(this.graph.getView().getOverlayPane());
                this.focusHighlight = c;
                h = mxUtils.bind(this,
                function() {
                    return this.currentFocus != null ? this.currentFocus: a.getState()
                });
                mxEvent.redirectMouseEvents(c.node, this.graph, h)
            } else {
                this.focusHighlight.bounds = c;
                this.focusHighlight.redraw()
            }
            break
        }
        if (this.currentConstraint == null && this.focusHighlight != null) {
            this.focusHighlight.destroy();
            this.focusHighlight = null
        }
    }
};
mxConstraintHandler.prototype.destroy = function() {
    this.reset()
};